#include "LandmarkDetector.h"
#include "highgui.h"
#include <iostream>

using namespace std;

LandmarkDetector::LandmarkDetector(string imageName, Rect rect) : faceRect(rect) {
	image = cvLoadImage(imageName.c_str());
}

LandmarkDetector::~LandmarkDetector() {
	cvReleaseImage(&image);
}

vector<Rect*> LandmarkDetector::getNose() {
	
	// Create memory for calculations
	static CvMemStorage* storage = 0;

	// Create a new Haar classifier
	static CvHaarClassifierCascade* cascade = 0;
	cascade = (CvHaarClassifierCascade*)cvLoad("../util/cascades/haarcascade_mcs_nose.xml");

	// Read scale
	int scale = 1;
	
	// Create a new image based on the input image
	IplImage* temp = cvCreateImage(cvSize(image->width/scale, image->height/scale), 8, 3 );
	cvCopy(image, temp);
	
	float topX = faceRect.getTopLeft().getX();
	float topY = faceRect.getTopLeft().getY() + (faceRect.getBottomRight().getY() - faceRect.getTopLeft().getY()) / 4;
	cvSetImageROI(temp, cvRect(topX, topY, 
						 		faceRect.getBottomRight().getX() - faceRect.getTopLeft().getX(), 
								(faceRect.getBottomRight().getY() - faceRect.getTopLeft().getY()) / 2));
	
	// Create result array
	vector<Rect*> result;

	// Create two points to represent the face locations
	CvPoint pt1, pt2;
	
	// Check whether the cascade has loaded successfully. Else report and error and quit
	if(!cascade) {
		cout << "ERROR: Could not load classifier cascade" << endl;
		return result;
	}
	
	// Allocate the memory storage
	storage = cvCreateMemStorage(0);

	// Clear the memory storage which was used before
	cvClearMemStorage(storage);

	// There can be more than one face in an image. So create a growable sequence of faces.
	// Detect the objects and store them in the sequence
	CvSeq* noses = cvHaarDetectObjects(temp, cascade, storage, 1.15, 3, 0, cvSize(25, 25));
	
	
	// If there is no 1 nose detected do not add anything
	if (noses->total == 1) {
		
		// Create a new rectangle for drawing the face
		CvRect* r = (CvRect*)cvGetSeqElem(noses, 0);

		// Find the dimensions of the face,and scale it if necessary
		pt1.x = r->x * scale;
		pt2.x = (r->x + r->width) * scale;
		pt1.y = r->y * scale;
		pt2.y = (r->y + r->height) * scale;
	
		// Create rect
		Rect* rect = new Rect(Point(r->x * scale + topX, r->y * scale + topY), Point((r->x + r->width) * scale + topX, (r->y + r->height) * scale + topY));
		result.push_back(rect);

		// Draw the rectangle in the input image
		cvRectangle(temp, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0 );
	}
	
	// Release the temp image created.
	cvReleaseImage(&temp);
	
	return result;
}

vector<Rect*> LandmarkDetector::getEyes() {
	
	// Create memory for calculations
	static CvMemStorage* storage = 0;
	
	// Create a new Haar classifier
	static CvHaarClassifierCascade* cascade = 0;
	cascade = (CvHaarClassifierCascade*)cvLoad("../util/cascades/haarcascade_eye.xml");

	// Read scale
	int scale = 1;
	
	// Create a new image based on the input image
	IplImage* temp = cvCreateImage(cvSize(image->width/scale, image->height/scale), 8, 3 );
	cvCopy(image, temp);
	
	float topX = faceRect.getTopLeft().getX();
	float topY = faceRect.getTopLeft().getY();
	cvSetImageROI(temp, cvRect(topX, topY, 
						 		faceRect.getBottomRight().getX() - faceRect.getTopLeft().getX(), 
								(faceRect.getBottomRight().getY() - faceRect.getTopLeft().getY()) / 2));
	
	// Create result array
	vector<Rect*> result;

	// Create two points to represent the face locations
	CvPoint pt1, pt2;
	
	// Check whether the cascade has loaded successfully. Else report and error and quit
	if(!cascade) {
		cout << "ERROR: Could not load classifier cascade" << endl;
		return result;
	}
	
	// Allocate the memory storage
	storage = cvCreateMemStorage(0);

	// Clear the memory storage which was used before
	cvClearMemStorage(storage);

	// There can be more than one face in an image. So create a growable sequence of faces.
	// Detect the objects and store them in the sequence
	CvSeq* eyes = cvHaarDetectObjects(temp, cascade, storage, 1.05, 3, 0, cvSize(18, 18));
	
	// If there are more than 2 eyes do not add any
	if (eyes->total <= 2) {
		// Loop the number of eyes found.
		for(int i = 0; i < (eyes ? eyes->total : 0); i++ ) {
		
			// Create a new rectangle for drawing the face
			CvRect* r = (CvRect*)cvGetSeqElem(eyes, i );

			// Find the dimensions of the face,and scale it if necessary
			pt1.x = r->x * scale;
			pt2.x = (r->x + r->width) * scale;
			pt1.y = r->y * scale;
			pt2.y = (r->y + r->height) * scale;
		
			// Create rect
			Rect* rect = new Rect(Point(r->x * scale + topX, r->y * scale + topY), Point((r->x + r->width) * scale + topX, (r->y + r->height) * scale + topY));
			result.push_back(rect);

			// Draw the rectangle in the input image
			cvRectangle(temp, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0 );
		}
	}
	
	// Release the temp image created.
	cvReleaseImage(&temp);
	
	return result;
}

vector<Rect*> LandmarkDetector::getMouth() {
	
	// Create memory for calculations
	static CvMemStorage* storage = 0;

	// Create a new Haar classifier
	static CvHaarClassifierCascade* cascade = 0;
	cascade = (CvHaarClassifierCascade*)cvLoad("../util/cascades/haarcascade_mcs_mouth.xml");

	// Read scale
	int scale = 1;
	
	// Create a new image based on the input image
	IplImage* temp = cvCreateImage(cvSize(image->width/scale, image->height/scale), 8, 3 );
	cvCopy(image, temp);
	
	float topX = faceRect.getTopLeft().getX();
	float topY = faceRect.getTopLeft().getY() + (faceRect.getBottomRight().getY() - faceRect.getTopLeft().getY()) * 2 / 3;
	cvSetImageROI(temp, cvRect(topX, topY, 
						 		faceRect.getBottomRight().getX() - faceRect.getTopLeft().getX(), 
								(faceRect.getBottomRight().getY() - faceRect.getTopLeft().getY()) * 1 / 3));
	
	// Create result array
	vector<Rect*> result;

	// Create two points to represent the face locations
	CvPoint pt1, pt2;
	
	// Check whether the cascade has loaded successfully. Else report and error and quit
	if(!cascade) {
		cout << "ERROR: Could not load classifier cascade" << endl;
		return result;
	}
	
	// Allocate the memory storage
	storage = cvCreateMemStorage(0);

	// Clear the memory storage which was used before
	cvClearMemStorage(storage);

	// There can be more than one face in an image. So create a growable sequence of faces.
	// Detect the objects and store them in the sequence
	CvSeq* mouthes = cvHaarDetectObjects(temp, cascade, storage, 1.25, 4, 0, cvSize(30, 15));
	
	// If there are than more one mouth do nothing
	if (mouthes->total == 1) {
		// Create a new rectangle for drawing the mouth
		CvRect* r = (CvRect*)cvGetSeqElem(mouthes, 0);

		// Find the dimensions of the mouth,and scale it if necessary
		pt1.x = r->x*scale;
		pt2.x = (r->x+r->width)*scale;
		pt1.y = r->y*scale;
		pt2.y = (r->y+r->height)*scale;
		
		// Create rect
		Rect* rect = new Rect(Point(r->x * scale + topX, r->y * scale + topY), Point((r->x + r->width) * scale + topX, (r->y + r->height) * scale + topY));
		result.push_back(rect);

		// Draw the rectangle in the input image
		cvRectangle(temp, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0 );
	}
	
	// Release the temp image created.
	cvReleaseImage(&temp);
	
	return result;
}

vector<Rect*> LandmarkDetector::getEyesPair() {
	// Create memory for calculations
	static CvMemStorage* storage = 0;

	// Create a new Haar classifier
	static CvHaarClassifierCascade* cascade = 0;
	cascade = (CvHaarClassifierCascade*)cvLoad("../util/cascades/haarcascade_mcs_eyepair_big.xml");

	// Read scale
	int scale = 1;

	// Create a new image based on the input image
	IplImage* temp = cvCreateImage(cvSize(image->width/scale, image->height/scale), 8, 3 );
	cvCopy(image, temp);

	float topX = faceRect.getTopLeft().getX();
	float topY = faceRect.getTopLeft().getY();
	cvSetImageROI(temp, cvRect(topX, topY, 
						 		faceRect.getBottomRight().getX() - faceRect.getTopLeft().getX(), 
								(faceRect.getBottomRight().getY() - faceRect.getTopLeft().getY()) / 2));

	// Create result array
	vector<Rect*> result;

	// Create two points to represent the face locations
	CvPoint pt1, pt2;

	// Check whether the cascade has loaded successfully. Else report and error and quit
	if(!cascade) {
		cout << "ERROR: Could not load classifier cascade" << endl;
		return result;
	}

	// Allocate the memory storage
	storage = cvCreateMemStorage(0);

	// Clear the memory storage which was used before
	cvClearMemStorage(storage);

	// There can be more than one face in an image. So create a growable sequence of faces.
	// Detect the objects and store them in the sequence
	CvSeq* eyePairs = cvHaarDetectObjects(temp, cascade, storage, 1.05, 3, 0, cvSize(60, 15));
	
	// There should be only one eye pair
	if (eyePairs->total == 1) {
	
		// Create a new rectangle for drawing the eye pair
		CvRect* r = (CvRect*)cvGetSeqElem(eyePairs, 0);

		// Find the dimensions of the eye pair,and scale it if necessary
		pt1.x = r->x*scale;
		pt2.x = (r->x+r->width)*scale;
		pt1.y = r->y*scale;
		pt2.y = (r->y+r->height)*scale;
	
		// Create rect
		Rect* rect = new Rect(Point(r->x * scale + topX, r->y * scale + topY), Point((r->x + r->width) * scale + topX, (r->y + r->height) * scale + topY));
		result.push_back(rect);

		// Draw the rectangle in the input image
		cvRectangle(temp, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0 );
	}

	// Release the temp image created.
	cvReleaseImage(&temp);

	return result;
}